#include "qextserialport.h"
#include "qextserialenumerator.h"
#include "dialog.h"
#include "ui_dialog.h"
#include <QtCore>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

using namespace std;

Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog),
    chart(new QChart),
    lineSeries(new QLineSeries),
    scatterSeries(new QScatterSeries)
{
    ui->setupUi(this);

    //! [0]
    foreach (QextPortInfo info, QextSerialEnumerator::getPorts())
        ui->portBox->insertItem(0,info.portName);   // addItem(info.portName);
    ui->portBox->setCurrentIndex(0);
    //make sure user can input their own port name!
    ui->portBox->setEditable(true);
    ui->baudRateBox->clear();
    ui->baudRateBox->addItem("9600", BAUD9600);
    ui->baudRateBox->addItem("19200", BAUD19200);
    ui->baudRateBox->addItem("38400", BAUD38400);
    ui->baudRateBox->addItem("57600", BAUD57600);
    ui->baudRateBox->addItem("115200", BAUD115200);
    ui->baudRateBox->setCurrentIndex(4);

    ui->parityBox->addItem("NONE", PAR_NONE);
    ui->parityBox->addItem("ODD", PAR_ODD);
    ui->parityBox->addItem("EVEN", PAR_EVEN);

    ui->dataBitsBox->addItem("5", DATA_5);
    ui->dataBitsBox->addItem("6", DATA_6);
    ui->dataBitsBox->addItem("7", DATA_7);
    ui->dataBitsBox->addItem("8", DATA_8);
    ui->dataBitsBox->setCurrentIndex(3);

    ui->stopBitsBox->addItem("1", STOP_1);
    ui->stopBitsBox->addItem("2", STOP_2);

    ui->queryModeBox->addItem("Polling", QextSerialPort::Polling);
    ui->queryModeBox->addItem("EventDriven", QextSerialPort::EventDriven);
    //! [0]
//    ui->maxXSizeBox->addItem("1000",1000);
    ui->maxXSizeBox->addItem("2000",2000);
//    ui->maxYSizeBox->addItem("1000",1000);
    ui->maxYSizeBox->addItem("2000",2000);

//    ui->gridLengthBox->addItem("0.1",0.1);
    ui->gridLengthBox->addItem("0.05",0.05);

//    ui->mapScaleBox->addItem("1",1);
    ui->mapScaleBox->addItem("2",2);

    ui->led->turnOff();

    timerReadData = new QTimer(this);
    timerReadData->setInterval(40);       // 40ms update slam data
    timerUpdateStatus = new QTimer(this);
    timerUpdateStatus->setInterval(200);       // 1000ms update drone status


    //! [1]
    PortSettings settings = {BAUD115200, DATA_8, PAR_NONE, STOP_1, FLOW_OFF, 10};
    port = new QextSerialPort(ui->portBox->currentText(), settings, QextSerialPort::Polling);
    //! [1]

    enumerator = new QextSerialEnumerator(this);
    enumerator->setUpNotifications();

    connect(ui->baudRateBox, SIGNAL(currentIndexChanged(int)), SLOT(onBaudRateChanged(int)));
    connect(ui->parityBox, SIGNAL(currentIndexChanged(int)), SLOT(onParityChanged(int)));
    connect(ui->dataBitsBox, SIGNAL(currentIndexChanged(int)), SLOT(onDataBitsChanged(int)));
    connect(ui->stopBitsBox, SIGNAL(currentIndexChanged(int)), SLOT(onStopBitsChanged(int)));
    connect(ui->queryModeBox, SIGNAL(currentIndexChanged(int)), SLOT(onQueryModeChanged(int)));
    connect(ui->timeoutBox, SIGNAL(valueChanged(int)), SLOT(onTimeoutChanged(int)));
    connect(ui->portBox, SIGNAL(editTextChanged(QString)), SLOT(onPortNameChanged(QString)));
    connect(ui->openCloseButton, SIGNAL(clicked()), SLOT(onOpenCloseButtonClicked()));
    connect(ui->sendButton, SIGNAL(clicked()), SLOT(onSendButtonClicked()));
    connect(ui->maxXSizeBox, SIGNAL(currentIndexChanged(int)), SLOT(onMaxXSizeChanged(int)));
    connect(ui->maxYSizeBox, SIGNAL(currentIndexChanged(int)), SLOT(onMaxYSizeChanged(int)));
    connect(ui->gridLengthBox, SIGNAL(currentIndexChanged(int)), SLOT(onGridLengthChanged(int)));
    connect(ui->mapScaleBox, SIGNAL(currentIndexChanged(int)), SLOT(onMapScaleChanged(int)));

    connect(timerReadData, SIGNAL(timeout()), SLOT(onReadyRead()));
    connect(timerUpdateStatus, SIGNAL(timeout()), SLOT(onUpdateStatus()));
    connect(port, SIGNAL(readyRead()), SLOT(onReadyRead()));
    connect(port, SIGNAL(readyRead()), SLOT(onUpdateStatus()));

    connect(enumerator, SIGNAL(deviceDiscovered(QextPortInfo)), SLOT(onPortAddedOrRemoved()));
    connect(enumerator, SIGNAL(deviceRemoved(QextPortInfo)), SLOT(onPortAddedOrRemoved()));

    max_x_size = 2000;
    max_y_size = 2000;
    grid_length = 0.05;
    map_scale = 2;

    initChart();
}

Dialog::~Dialog()
{
    delete ui;
    delete port;
    delete scatter;
}

void Dialog::initChart()
{

    scatter = new Scatter(ui->slamWidget);
    setWindowTitle(tr("地面站 －－ SLAM 显示"));
    setWindowIcon(QIcon(":/icons/qt-logo.png"));
    chart->setBackgroundBrush(QBrush(Qt::white));  //darkGreen
//    chart->setBackgroundBrush(QBrush(QColor(0, 255, 0)));
    chart->setBackgroundRoundness(0);

    const QColor bk=QColor(Qt::black);   // yellow
    QPen penYellow;
    penYellow.setColor(bk);
    penYellow.setStyle(Qt::SolidLine);
    penYellow.setWidth(1);
    scatterSeries->setPen(penYellow);
    scatterSeries->setColor(bk);
    scatterSeries->setMarkerSize(1);
    scatterSeries->setName("SLAM");
    chart->addSeries(scatterSeries);

//    const QColor red=QColor(255,0,0);
    QPen penRed;
    penRed.setColor(Qt::red);
    penRed.setWidth(2);
//    lineSeries->setColor(red);
    lineSeries->setPen(penRed);
    lineSeries->setName("无人机");
    chart->addSeries(lineSeries);
//    lineSeries->setUseOpenGL(true);

    chart->createDefaultAxes();
    chart->axes(Qt::Horizontal).first()->setRange(900, 1100);
    chart->axes(Qt::Vertical).first()->setRange(900, 1100);
    chart->axes(Qt::Horizontal).first()->setGridLineVisible(false);
    chart->axes(Qt::Vertical).first()->setGridLineVisible(false);
    chart->axes(Qt::Horizontal).first()->setLinePenColor(Qt::yellow);
    chart->axes(Qt::Vertical).first()->setLinePenColor(Qt::yellow);

    /* hide legend */
    chart->legend()->show();

    myChartView = new MyChartView(chart);
    myChartView->setRenderHint(QPainter::Antialiasing);

    ui->chartViewLayout->addWidget(myChartView);
}

void Dialog::changeEvent(QEvent *e)
{
    QDialog::changeEvent(e);
    switch (e->type()) {
    case QEvent::LanguageChange:
        ui->retranslateUi(this);
        break;
    default:
        break;
    }
}

void Dialog::wheelEvent(QWheelEvent *event)
{
    if (event->delta() > 0) {
        chart->zoom(1.1);
    } else {
        chart->zoom(10.0/11);
    }

    QWidget::wheelEvent(event);
}

void Dialog::onPortNameChanged(const QString & /*name*/)
{
    if (port->isOpen()) {
        port->close();
        ui->openCloseButton->setText("打开串口");
        ui->led->turnOff();
    }
}
//! [2]
void Dialog::onBaudRateChanged(int idx)
{
    port->setBaudRate((BaudRateType)ui->baudRateBox->itemData(idx).toInt());
}

void Dialog::onParityChanged(int idx)
{
    port->setParity((ParityType)ui->parityBox->itemData(idx).toInt());
}

void Dialog::onDataBitsChanged(int idx)
{
    port->setDataBits((DataBitsType)ui->dataBitsBox->itemData(idx).toInt());
}

void Dialog::onStopBitsChanged(int idx)
{
    port->setStopBits((StopBitsType)ui->stopBitsBox->itemData(idx).toInt());
}

void Dialog::onQueryModeChanged(int idx)
{
    port->setQueryMode((QextSerialPort::QueryMode)ui->queryModeBox->itemData(idx).toInt());
}

void Dialog::onMaxXSizeChanged(int idx)
{
    max_x_size = ui->maxXSizeBox->itemData(idx).toInt();
}

void Dialog::onMaxYSizeChanged(int idx)
{
    max_y_size = ui->maxYSizeBox->itemData(idx).toInt();
}

void Dialog::onGridLengthChanged(int idx)
{
    grid_length = ui->gridLengthBox->itemData(idx).toDouble();
}

void Dialog::onMapScaleChanged(int idx)
{
    map_scale = ui->mapScaleBox->itemData(idx).toInt();
}


void Dialog::onTimeoutChanged(int val)
{
    port->setTimeout(val);
}

void Dialog::onUpdateStatus()
{
    const QString strHeight = QString::number(uint32_t(pp.z)/1);
    ui->heightEdit->setText(strHeight);
    const QString strYaw = QString::number(pp.yaw/1000);
    ui->yawEdit->setText(strYaw);
    const QString strXDrone = QString::number(pp.x/1000/grid_length); //
    ui->xDroneEdit->setText(strXDrone);
    const QString strYDrone = QString::number(pp.y/1000/grid_length);
    ui->yDroneEdit->setText(strYDrone);
}
//! [2]
//! [3]
void Dialog::onOpenCloseButtonClicked()
{
    if (!port->isOpen()) {
        port->setPortName(ui->portBox->currentText());
        port->open(QIODevice::ReadWrite);
        //
        map_get_pack mgp;
        mgp.check_sum = mgp.id;
        QByteArray h1, h2, msg;
        h1.append(header1);
        h2.append(header2);
        msg.append((char *)&mgp);
        port->write(h1);
        port->write(h2);
        port->write(msg);
//        port->write((uint8_t*)&mgp, sizeof(map_get_pack));

        ui->openCloseButton->setText("关闭串口");
    }
    else {
        port->close();
        ui->openCloseButton->setText("打开串口");
    }

    //If using polling mode, we need a QTimer
    if (port->isOpen() && port->queryMode() == QextSerialPort::Polling){
        timerReadData->start();
        timerUpdateStatus->start();
    }
    else{
        timerReadData->stop();
        timerUpdateStatus->stop();
    }

    //update led's status
    ui->led->turnOn(port->isOpen());
}
//! [3]

void Dialog::onSendButtonClicked()
{
    if (port->isOpen() && !ui->sendEdit->toPlainText().isEmpty())
        port->write(ui->sendEdit->toPlainText().toLatin1());
}

void Dialog::onPortAddedOrRemoved()
{
    QString current = ui->portBox->currentText();

    ui->portBox->blockSignals(true);
    ui->portBox->clear();
    foreach (QextPortInfo info, QextSerialEnumerator::getPorts())
        ui->portBox->addItem(info.portName);

    ui->portBox->setCurrentIndex(ui->portBox->findText(current));

    ui->portBox->blockSignals(false);
}
//![4]

void Dialog::onReadyRead()
{
    if (port->bytesAvailable()) {
        QByteArray current_bytes = port->readAll();
        if(current_bytes.size()<7)   // at least 7
            return;
// for comm settings
        if(ui->checkBoxDisp->isChecked())
        {
            ui->recvEdit->insertPlainText(current_bytes.toHex().toUpper());   //fromLatin1
            ui->recvEdit->insertPlainText("\n");
            ui->recvEdit->moveCursor(QTextCursor::End);
        }
// for parse
        // check headers
        if((uint8_t)current_bytes.at(0)!=header1 || (uint8_t)current_bytes.at(1)!=header2)
            return;
        current_bytes.remove(0,2);   // remove the headers, it's not included in pack.
        uint8_t type = (uint8_t)current_bytes.at(0);
        cell_pack cp_slam(true);
        cell_pack cp_clear(false);
        uint8_t *p, check_sum = type;
        uint16_t xPos, yPos;       // 飞机位置
        switch (type) {
        case ID_CELL_SET_PACK:
            memcpy((char*)&cp_slam, current_bytes, sizeof(cell_pack) );
            p = (uint8_t*)&cp_slam;
            for (int i = 1; i < sizeof(cell_pack) - 1; ++i)
            {
                check_sum ^= p[i];
            }
//            check_sum = cp_slam.check_sum;   // drop check_sum
            if (check_sum == cp_slam.check_sum && cp_slam.x < max_x_size && cp_slam.y < max_y_size)
            {
                qDebug()<<"Slam data:"<<cp_slam.x<<cp_slam.y;
                uint16_t *xSlam = &cp_slam.x, *ySlam = &cp_slam.y;
                plotScatterOfSLAM(xSlam,ySlam);
            }
            break;
        case ID_CELL_CLEAR_PACK:
            memcpy((char*)&cp_clear, current_bytes, sizeof(map_clear_pack) );
            p = (uint8_t*)&cp_clear;
            for (int i = 1; i < sizeof(map_clear_pack) - 1; ++i)
            {
                check_sum ^= p[i];
            }
//            check_sum = cp_clear.check_sum;   // drop check_sum
            if (check_sum == cp_clear.check_sum && cp_clear.x < max_x_size && cp_clear.y < max_y_size)
            {
//                map[cp.y][cp.x] = false;
                //map[cp.x][cp.y] = false;
            }
            break;
        case ID_POS_PACK:
            memcpy((char*)&pp, current_bytes, sizeof(pos_pack) );
            p = (uint8_t*)&pp;
            for (int i = 1; i < sizeof(pos_pack) - 1; ++i)
            {
                check_sum ^= p[i];
            }
            //TRACE(_T("check_sum/pp.check_sum=%d/%d\t,pp.x=%d\t,map_x_size=%d\n"),
//            check_sum = pp.check_sum;   // drop check_sum
            xPos = uint32_t(pp.x / 1000.0 / grid_length);
            yPos = uint32_t(pp.y / 1000.0 / grid_length);
            if (check_sum == pp.check_sum && xPos < max_x_size && yPos < max_y_size)
            {
                qDebug()<<"Drone Pos:"<<xPos<<yPos;

                xDrone = &xPos;
                yDrone = &yPos;
                plotLineOfDrone(xDrone,yDrone);
            }
            break;
        case ID_MAP_CLEAR_PACK:
        {
            memcpy(&check_sum, current_bytes, 1 );

            if (type == check_sum)
            {
                for (uint32_t i = 0; i < max_x_size; ++i)
                {
                    for (uint32_t j = 0; j < max_y_size; ++j)
                    {
//                        map[j][i] = false;
                        //map[i][j] = false;
                    }
                }
            }
        }
            break;
        default:
            break;
        }

// for plot

    }
}


// 散点
void Dialog::plotScatterOfSLAM(uint16_t *xSlam, uint16_t *ySlam)
{
    scatter->PlotSCatter(ySlam, xSlam);   // 整体视图显示用

    // 局部视图显示用。
    qreal x, y;
    x = *xSlam; y = *ySlam;
    scatterSeries->append(y, x);
//    for(int i=0;i<100;i++)   // 为测试生成的随机点
//    {
//        QTime time= QTime::currentTime();
//        qsrand(time.msec()+time.second()*1000);
//        x=450+qrand()%100;
//        y=450+qrand()%100;
//        scatterSeries->append(x, y);
//    }
    //    chart->addSeries(scatterSeries);

}

// 连线
void Dialog::plotLineOfDrone(uint16_t *xDrone,uint16_t *yDrone)
{
    scatter->DrawLines(yDrone, xDrone);
    qreal x, y;
    x = *xDrone; y = *yDrone;
    lineSeries->append(y,x);
//    for(int i=0;i<20;i++)   // 为测试生成的随机点
//    {
//        QTime time= QTime::currentTime();
//        qsrand(time.msec()+time.second()*1000);
//        x=450+qrand()%100;
//        y=450+qrand()%100;
//        lineSeries->append(x, y);
//    }
}


//! [5]
